/*
 * Copyright (c) 2024 OpenHiTLS. All rights reserved.
 * Licensed under the OpenHiTLS license.
 */
package org.openhitls.core;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

/**
 * Utility class for loading the native JNI library.
 *
 * <p>Loading strategy:</p>
 * <ol>
 *   <li>Try to load from system library path (System.loadLibrary)</li>
 *   <li>Fall back to extracting from JAR and loading from temp directory</li>
 * </ol>
 */
public class NativeLoader {
    private static volatile boolean loaded = false;
    private static final Object LOAD_LOCK = new Object();

    /**
     * Load the native library. This method is idempotent and thread-safe.
     */
    public static void load() {
        if (loaded) {
            return;
        }

        synchronized (LOAD_LOCK) {
            if (loaded) {
                return;
            }

            // Try loading from system library path first
            try {
                System.loadLibrary("hitls_crypto_jni");
                loaded = true;
                return;
            } catch (UnsatisfiedLinkError e) {
                // Fall back to loading from JAR
            }

            // Extract and load from JAR
            loadFromJar();
            loaded = true;
        }
    }

    /**
     * Check if the native library has been loaded.
     *
     * @return true if the library is loaded
     */
    public static boolean isLoaded() {
        return loaded;
    }

    private static void loadFromJar() {
        String osName = System.getProperty("os.name").toLowerCase();
        String osArch = System.getProperty("os.arch").toLowerCase();

        String libName;
        String platform;

        // Determine library name and platform path
        if (osName.contains("linux")) {
            libName = "libhitls_crypto_jni.so";
            if (osArch.contains("amd64") || osArch.contains("x86_64")) {
                platform = "linux-x86_64";
            } else if (osArch.contains("aarch64") || osArch.contains("arm64")) {
                platform = "linux-aarch64";
            } else {
                throw new UnsatisfiedLinkError("Unsupported Linux architecture: " + osArch);
            }
        } else if (osName.contains("mac") || osName.contains("darwin")) {
            libName = "libhitls_crypto_jni.dylib";
            if (osArch.contains("amd64") || osArch.contains("x86_64")) {
                platform = "darwin-x86_64";
            } else if (osArch.contains("aarch64") || osArch.contains("arm64")) {
                platform = "darwin-aarch64";
            } else {
                throw new UnsatisfiedLinkError("Unsupported macOS architecture: " + osArch);
            }
        } else if (osName.contains("windows")) {
            libName = "hitls_crypto_jni.dll";
            if (osArch.contains("amd64") || osArch.contains("x86_64")) {
                platform = "windows-x86_64";
            } else {
                throw new UnsatisfiedLinkError("Unsupported Windows architecture: " + osArch);
            }
        } else {
            throw new UnsatisfiedLinkError("Unsupported operating system: " + osName);
        }

        String libPath = "/META-INF/native/" + platform + "/" + libName;

        try (InputStream in = NativeLoader.class.getResourceAsStream(libPath)) {
            if (in == null) {
                throw new UnsatisfiedLinkError("Native library not found in JAR: " + libPath);
            }

            Path tempDir = Files.createTempDirectory("hitls4j-native");
            tempDir.toFile().deleteOnExit();
            File tempFile = new File(tempDir.toFile(), libName);
            tempFile.deleteOnExit();

            Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            System.load(tempFile.getAbsolutePath());
        } catch (IOException e) {
            throw new RuntimeException("Failed to load native library from JAR", e);
        }
    }
}
